package com.funtask.fun.utils;

import lombok.extern.slf4j.Slf4j;
import org.jooq.lambda.Unchecked;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.stereotype.Component;

import java.beans.PropertyDescriptor;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 工具类：实体对象操作
 *
 * @author henry zhangqi
 */
@Slf4j
@Component
public class BeanUtil {

    /**
     * 拷贝非空值属性
     *
     * @param src    源对象
     * @param target 目标对象
     */
    public static void copyProperties(Object src, Object target) {
        Optional.ofNullable(src).ifPresent(item -> BeanUtils.copyProperties(item, target, getNullPropertyNames(item)));
    }

    /**
     * 属性差集
     *
     * @param newObject 新对象
     * @param oldObject 旧对象
     */
    public static String differenceProperties(Object newObject, Object oldObject) {
        Map<String, String> resultMap = new HashMap<>(16);
        Arrays.stream(newObject.getClass().getDeclaredFields()).forEach(Unchecked.consumer(newField -> {
            newField.setAccessible(true);
            Object newValue = newField.get(newObject);
            Arrays.stream(oldObject.getClass().getDeclaredFields()).filter(oldField -> newField.getName().equals(oldField.getName())).forEach(Unchecked.consumer(oldField -> {
                oldField.setAccessible(true);
                Object oldValue = oldField.get(oldObject);
                if (oldValue != null && newValue != null && !oldValue.equals(newValue)) {
                    resultMap.put(oldField.getName(), oldValue.toString() + "->" + newValue.toString());
                }
            }));
        }));
        return resultMap.toString();
    }

    /**
     * 赋值
     *
     * @param tClass 类
     * @param e      对象
     * @param <T>    类泛型
     * @param <E>    对象泛型
     * @return <T, E> T
     */
    public static <T, E> Optional<T> getClass(Class<T> tClass, E e) {
        return Optional.ofNullable(e).map(Unchecked.function(item -> {
            T temp = tClass.newInstance();
            copyProperties(item, temp);
            return temp;
        }));
    }

    /**
     * 获取赋值列表
     *
     * @param tClass 类
     * @param es     对象列表
     * @param <T>    类泛型
     * @param <E>    对象泛型
     * @return List<T>
     */
    public static <T, E> List<T> getClass(Class<T> tClass, List<E> es) {
        return Optional.ofNullable(es).orElse(new ArrayList<>()).stream().filter(e -> getClass(tClass, e).isPresent()).map(e -> getClass(tClass, e).orElse(null)).collect(Collectors.toList());
    }

    /**
     * 获取对象里的空值属性
     *
     * @param source 目标对象
     * @return String[]
     */
    private static String[] getNullPropertyNames(Object source) {
        final BeanWrapper src = new BeanWrapperImpl(source);
        return Arrays.stream(src.getPropertyDescriptors()).filter(pd -> src.getPropertyValue(pd.getName()) == null).map(PropertyDescriptor::getName).distinct().toArray(String[]::new);
    }
}
